# -*- coding: utf-8 -*-
import os
import re
import sys
import time
import threading
import random
import docx
import csv
import xlwt
import openpyxl
import tkinter as tk
from tkinter import filedialog
import tkinter.messagebox as messagebox
import xml.etree.ElementTree as ET
import pandas as pd
from pystardict import Dictionary


class Dict:
    def __init__(self, dict_path):
        self.dict = Dictionary(dict_path)

    def __split_and_join(self, text):
        max_len = 30
        lines = text.split('\n')  # 按行拆分字符串
        new_lines = []
        for line in lines:
            if len(line) > max_len:  # 若某行长度大于60
                while len(line) > max_len:
                    new_lines.append(line[:max_len])  # 拆成两行
                    line = line[max_len:]
            new_lines.append(line)
        return '\n'.join(new_lines)

    def search(self, word):
        res = self.dict.get(word)
        res = res.replace('*', '')
        res = re.sub(r'相关词组:.*', '', res, flags=re.DOTALL)
        if res.endswith('\n'):
            res = res[:-1]
        return self.__split_and_join(res)


def display_in_center(window, window_size=(200,100), window_offset=(0,0)):
    window_width = window_size[0]
    window_height = window_size[1]
    screen_width = window.winfo_screenwidth() + window_offset[0]
    screen_height = window.winfo_screenheight() + window_offset[1]
    x = int((screen_width - window_width) / 2)
    y = int((screen_height - window_height) / 2)
    window.geometry(f"{window_width}x{window_height}+{x}+{y}")
    return window_width, window_height


def show_help():
    help_window = tk.Toplevel()
    help_window.title("帮助")
    help_text = '''
单词浮窗 v0.0.7

这是一款可以边看剧边刷单词的浮窗工具
只需导入单词表文件，点击开始即可刷词
单词表文件制作方法：每行一个单词，无需释义
支持多种文件类型：Excel, Word, txt, csv, 有道词典xml

快捷键说明：
上一个单词：k
下一个单词：j
标记为熟词不再显示：f
暂停/恢复播放：空格

作者：Mark Yang
邮箱：ideamark@qq.com
'''
    help_label = tk.Label(help_window, text=help_text)
    help_label.pack(padx=10, pady=10)
    display_in_center(help_window, (400, 280))


def browse_file():
    file_path = filedialog.askopenfilename()
    if file_path:
        file_path_var.set(file_path)
        file_name = file_path.split("/")[-1]
        word_list = get_word_list(file_path)
        words_count = len(word_list)
        text = f'{file_name} ({words_count}词)'
        file_path_label.config(text=text)
        return word_list
    return []


def get_word_list(file_path):
    def read_docx_lines():
        doc = docx.Document(file_path)
        lines = []
        for paragraph in doc.paragraphs:
            line = paragraph.text.strip()
            if line:  # 去除空行
                lines.append(line)
        return lines

    def read_youdao_xml():
        tree = ET.parse(file_path)
        root = tree.getroot()
        words = []
        for item in root.findall('item'):
            words.append(item.find('word').text.strip())
            # trans = item.find('trans').text.strip()
        return words

    ext = os.path.splitext(file_path)[1]
    try:
        if ext == '.csv':
            df = pd.read_csv(file_path)
            return df.iloc[:, 0].tolist()
        elif ext == '.xls' or ext == '.xlsx':
            df = pd.read_excel(file_path)
            return df.iloc[:, 0].tolist()
        elif ext == '.doc' or ext == '.docx':
            return read_docx_lines()
        elif ext == '.xml':
            return read_youdao_xml()
        else:
            with open(file_path, 'r', encoding='utf-8') as fp:
                lines = fp.readlines()
                return [line.strip() for line in lines if line.strip()]
    except FileNotFoundError:
        messagebox.showerror("Error", "读取文件出错")
        root.destroy()


def save_word_list(dictionary=None, file_path='', new_word_list=[]):
    def get_pure_def(word):
        definition = dictionary.search(word).replace('\n', ' ')
        definition = re.sub(r'\[.*?\]', '', definition)
        definition = re.sub(r'【.*', '', definition)
        return definition

    file_types = [("Excel", "*.xlsx"), 
                  ("Excel old", "*.xls"), 
                  ("Word", "*.docx"), 
                  ("Word old", "*.doc"), 
                  ("文本", "*.txt"), 
                  ("CSV", "*.csv"), 
                  ("有道词典 xml", "*.xml")]
    file_name = os.path.basename(file_path)
    file_type = filedialog.asksaveasfile(mode='w', filetypes=file_types, initialfile=file_name)

    if file_type:
        save_path = file_type.name
        if save_path.endswith('.csv'):
            with open(save_path, 'w', encoding='utf-8') as fp:
                writer = csv.writer(fp)
                for word in new_word_list:
                    writer.writerow([word])
                    
        elif save_path.endswith('.xls'):
            workbook = xlwt.Workbook()
            sheet = workbook.add_sheet('New Words')
            for index, word in enumerate(new_word_list):
                sheet.write(index, 0, word)
                sheet.write(index, 1, get_pure_def(word))
            workbook.save(save_path)
            
        elif save_path.endswith('.xlsx'):
            workbook = openpyxl.Workbook()
            sheet = workbook.active
            for index, word in enumerate(new_word_list):
                sheet.cell(row=index+1, column=1, value=word)
                sheet.cell(row=index+1, column=2, value=get_pure_def(word))
            workbook.save(save_path)
            
        elif save_path.endswith('.doc'):
            doc = docx.Document()
            for word in new_word_list:
                doc.add_paragraph(word)
            doc.save(save_path)
            
        elif save_path.endswith('.docx'):
            doc = docx.Document()
            for word in new_word_list:
                doc.add_paragraph(word)
            doc.save(save_path)
            
        elif save_path.endswith('.xml'):
            file_name = os.path.basename(save_path)  # 获取文件名，包括后缀
            tag = os.path.splitext(file_name)[0]  # 去掉后缀的文件名
            xml_str = ''
            for word in new_word_list:
                trans = get_pure_def(word)
                xml_str += f'''<item>
    <word>{word}</word>
    <trans><![CDATA[{trans}]]></trans>
    <phonetic><![CDATA[]]></phonetic>
    <lanfrom><![CDATA[en]]></lanfrom>
    <lanto><![CDATA[zh-CHS]]></lanto>
    <tags>{tag}</tags>
    <progress>1</progress>
</item>'''
            with open(save_path, "w", encoding="utf-8") as file:
                file.write('<wordbook>' + xml_str + '</wordbook>')
            
        elif save_path.endswith('.txt'):
            with open(save_path, 'w', encoding='utf-8') as file:
                for word in new_word_list:
                    file.write(word + '\n')
        else:
            messagebox.showerror("错误", "不支持的文件类型")


def rotate_word_list(root=None, dictionary=None, file_path='', delay_time=2, is_random=False, auto_play=False):
    root.title('')
    window_width, window_height = display_in_center(root, (400, 90), (0, 60))

    word_label = tk.Label(root, font=('Arial', 28))
    word_label.pack()
    definition_label = tk.Label(root, font=('Arial', 16), anchor='w')
    definition_label.pack()
    progress_label = tk.Label(root, font=('Arial', 15))
    progress_label.pack()
    default_text_color = word_label.cget('foreground')

    known_words = set()
    word_list = new_word_list = get_word_list(file_path)
    word_list.sort()
    current_index = new_index = 0
    if is_random:
        random.shuffle(word_list)

    running = True  # 控制线程运行的标志
    running_lock = threading.Lock()  # 创建一个线程锁对象
    window_closed = False
    paused_flag = '▶'
    playing_flag = '⏸'

    def get_play_state():
        if auto_play:
            return playing_flag
        else:
            return paused_flag

    def show_word(index):
        nonlocal new_word_list
        nonlocal new_index
        new_word_list = [word for word in word_list if word not in known_words]
        word = word_list[index]
        new_index = next((i for i, new_word in enumerate(new_word_list) if new_word == word), new_index)
        progress_text = f'{get_play_state()}  {new_index+1}/{len(new_word_list)}'
        progress_label.config(text=progress_text, fg='grey')
        if index < len(word_list):
            if word in known_words:
                word_label.config(text=word, fg='grey')
                definition_label.config(text=dictionary.search(word), fg='grey')
            else:
                word_label.config(text=word, fg=default_text_color)
                definition_label.config(text=dictionary.search(word), fg=default_text_color)
            word_label_height = word_label.winfo_reqheight()
            definition_label_height = definition_label.winfo_reqheight()
            progress_label_height = progress_label.winfo_reqheight()
            if word_label_height + definition_label_height + progress_label_height > window_height:
                root.geometry(f'{window_width}x{word_label_height + definition_label_height + progress_label_height + 10}')
            else:
                root.geometry(f'{window_width}x{window_height}')
            root.update()

    def goto_previous_word(event):
        if auto_play: return
        nonlocal current_index
        current_index = (current_index - 1) % len(word_list)
        while word_list[current_index] in known_words:
            current_index = (current_index - 1) % len(word_list)
        show_word(current_index)

    def goto_next_word(event):
        if auto_play: return
        nonlocal current_index
        current_index = (current_index + 1) % len(word_list)
        while word_list[current_index] in known_words:
            current_index = (current_index + 1) % len(word_list)
        show_word(current_index)

    def set_remembered(event):
        nonlocal current_index
        word = word_list[current_index]
        if word in known_words:
            known_words.remove(word)
        else:
            known_words.add(word)
        show_word(current_index)

    def pause_or_resume(event):
        nonlocal auto_play
        nonlocal window_closed
        if auto_play:
            window_closed = True
            auto_play = False
            new_progress_text = progress_label.cget("text").replace(playing_flag, paused_flag)
        else:
            auto_play = True
            window_closed = False
            threading.Thread(target=auto_play_function, daemon=True).start()
            new_progress_text = progress_label.cget("text").replace(paused_flag, playing_flag)
        progress_label.config(text=new_progress_text, fg='grey')

    def handle_key_press(event):
        def avoid_conflict():
            nonlocal running
            with running_lock:  # 使用with语句来自动获取和释放锁
                running = False  # 暂停自动播放功能
            time.sleep(1.5)  # 延时防止冲突
            with running_lock:
                running = True  # 恢复自动播放功能
        threading.Thread(target=avoid_conflict, daemon=True).start()

    def auto_play_function():
        nonlocal current_index
        while True:
            if window_closed: break
            with running_lock:  # 在检查running之前获取锁
                if running:
                    show_word(current_index)
                    time.sleep(delay_time)
                    current_index = (current_index + 1) % len(word_list)

    def on_close_window():
        nonlocal window_closed
        window_closed = True
        if known_words:
            export_words = messagebox.askyesno("", "是否导出新单词表？")
            if export_words:
                save_word_list(dictionary, file_path, new_word_list)
        root.destroy()

    root.protocol("WM_DELETE_WINDOW", on_close_window)
    root.bind('k', goto_previous_word)
    root.bind('j', goto_next_word)
    root.bind('f', set_remembered)
    root.bind('<space>', pause_or_resume)
    root.bind('<KeyPress>', handle_key_press)

    if auto_play:
        threading.Thread(target=auto_play_function, daemon=True).start()
    else:
        show_word(current_index)


def start_rotate_word_list(dictionary):
    file_path = file_path_var.get()
    delay_time = int(delay_time_entry.get())
    is_random = is_random_var.get()
    auto_play = auto_play_var.get()
    if not file_path:
        messagebox.showerror("Error", "请先导入单词表文件")
        return
    rotate_word_list_window = tk.Toplevel()
    rotate_word_list_window.wm_attributes("-topmost", True)
    rotate_word_list_window.wm_attributes("-alpha", 0.8)  # 设置窗口透明度为80%
    rotate_word_list(rotate_word_list_window, dictionary, file_path, delay_time, is_random, auto_play)


def start_save_word_list(dictionary):
    file_path = file_path_var.get()
    if not file_path:
        messagebox.showerror("Error", "请先导入单词表文件")
        return
    word_list = get_word_list(file_path)
    save_word_list(dictionary, file_path, word_list)


if __name__ == '__main__':

    base_path = getattr(sys, '_MEIPASS', os.path.abspath(os.path.dirname(__file__)))
    print(f'BASE_PATH = {base_path}')
    print('PWD_FILES = ' + ', '.join(os.listdir(base_path)))
    dict_path = os.path.join(base_path, 'stardict/stardict-langdao-ec-gb-2.4.2/langdao-ec-gb')
    dict = Dict(dict_path)

    root = tk.Tk()
    root.title("单词浮窗")
    root.wm_attributes("-alpha", 0.95)
    display_in_center(root, (200, 360))

    file_path_var = tk.StringVar()
    file_path_var.set("")

    file_path_frame = tk.Frame(root)
    file_path_frame.pack()

    browse_button = tk.Button(file_path_frame, text="导入单词表", command=browse_file)
    browse_button.pack(pady=(20, 0))

    file_path_label = tk.Label(file_path_frame, text="", width=30)
    file_path_label.pack(pady=(5, 0))

    export_button = tk.Button(root, text="导出单词表", command=lambda: start_save_word_list(dict))
    export_button.pack(pady=(5, 0))

    is_random_var = tk.BooleanVar()
    is_random_checkbox = tk.Checkbutton(root, text="随机顺序", variable=is_random_var)
    is_random_checkbox.pack(pady=(10, 0))

    auto_play_var = tk.BooleanVar()
    auto_play_checkbox = tk.Checkbutton(root, text="自动播放", variable=auto_play_var)
    auto_play_checkbox.pack(pady=(10, 0))

    delay_time_label = tk.Label(root, text="播放间隔/秒")
    delay_time_label.pack(pady=(10, 0))

    delay_time_entry = tk.Entry(root, width=2)
    delay_time_entry.pack(padx=10)
    delay_time_entry.insert(tk.END, "2")
    delay_time_entry.config(state=tk.DISABLED)

    start_button = tk.Button(root, text="开始", command=lambda: start_rotate_word_list(dict))
    start_button.pack(pady=(20, 0))

    help_button = tk.Button(root, text="帮助", command=show_help)
    help_button.pack(pady=(20, 0))

    def update_delay_time_entry_state():
        if auto_play_var.get():
            delay_time_entry.config(state=tk.NORMAL)
        else:
            delay_time_entry.config(state=tk.DISABLED)

    auto_play_var.trace('w', lambda *args: update_delay_time_entry_state())  # 监听auto_play_var变量的变化

    root.mainloop()
